package com.cyy.test_t_6_;

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * @Description 剑指Offer t_63- 数据流中的中位数
 * 题目描述
 * 如何得到一个数据流中的中位数？如果从数据流中读出奇数个数值，那么中位数就是所有数值排序之后位于中间的数值。
 * 如果从数据流中读出偶数个数值，那么中位数就是所有数值排序之后中间两个数的平均值。
 * 我们使用Insert()方法读取数据流，使用GetMedian()方法获取当前读取数据的中位数。
 *
 * 思路分析：
 * 用两个堆保存数据，保证两个堆的数据保持平衡（元素个数相差不超过1）大顶堆存放的数据要比小顶堆的数据小，
 *
 * 大顶堆存放的数据要比小顶堆的数据小当两个堆中元素为偶数个，将新加入元素加入到大顶堆，
 * 如果要加入的数据，比小顶堆的最小元素大，先将该元素插入小顶堆，然后将小顶堆的最小元素插入到大顶堆；
 *
 * 当两个堆中元素为奇数个，将新加入元素加入到小顶堆，
 * 如果要加入的数据，比大顶堆的最大元素小，先将该元素插入大顶堆，然后将大顶堆的最大元素插入到小顶堆。
 *
 * @Author Crystal
 * @Date 2019/7/23 14:56
 * @Version 1.0
 **/
public class t_63 {
    //用来记录两个堆中的数据量
    private static int count;
    //小顶堆
    private static PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    //大顶堆
    private static PriorityQueue<Integer> maxHeap = new PriorityQueue<>(11, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            //PriorityQueue默认是小顶堆，实现大顶堆，需要反转默认排序器
            return o2.compareTo(o1);
        }
    });

    public static void Insert(Integer num) {
        count ++;
        if((count & 1) == 0) { //当两个堆中元素为奇数个，将新加入元素加入到小顶堆
            //如果要加入的数据，比大顶堆的最大元素小，先将该元素插入大顶堆，然后将大顶堆的最大元素插入到小顶堆。
            if(!maxHeap.isEmpty() && num < maxHeap.peek()){
                maxHeap.offer(num);
                num = maxHeap.poll();
            }
            minHeap.offer(num);
        } else { //当两个堆中元素为偶数个，将新加入元素加入到大顶堆
            //如果要加入的数据，比小顶堆的最小元素大，先将该元素插入小顶堆，然后将小顶堆的最小元素插入到大顶堆；
            if(!minHeap.isEmpty() && num > minHeap.peek()) {
                minHeap.offer(num);
                num = minHeap.poll();
            }
            maxHeap.offer(num);
        }
    }

    public static Double GetMedian() {
        if(count == 0) {
            throw new RuntimeException("no available number!");
        }
        double result;
        //总数为奇数时，大顶堆堆顶就是中位数
        if((count & 1) == 1){
            result = maxHeap.peek();
        } else {
            result = (minHeap.peek() + maxHeap.peek())/2.0;
        }
        return result;
    }

    public static void main(String[] args) {
        int [] test = {1,2,5,6,3,9,8};
        Arrays.stream(test).forEach(e -> Insert(e));
        System.out.println(GetMedian());
    }
}
